import { Keys } from '@/constants'
import { localStg } from '@/utils'
import { JBoltApi } from '@/utils/request'
import type { Socket } from '@/typings/websocket'
import { useAppStore } from '../../../store'
import WebSocketHandler from './handler'
import { getServiceEnvConfig } from '~/.env-config'
const serverUrl: string = getServiceEnvConfig(import.meta.env).url
const state = {
    isOk: false,
    isConnecting: false,
    protocal: 'ws',
    host: serverUrl.substring(serverUrl.indexOf('//') + 2),
    ctx: '',
    url: '',
    //租户sn
    version: '0.1.0',
    tenantSn: '',
    binaryType: 'arraybuffer',
    reconnInterval: 5000,
    heartbeatTimeout: 10000,
    heartbeatSendInterval: 5000,
    //最后一次交互时间
    lastInteractionTime: 0,
    //心跳检测定时器
    pingInterval: 0
}
/**
 * 获取socket实例
 */
function getSocket(): Promise<Socket> {
    if (state.isConnecting) {
        return new Promise<Socket>((resolve) => {
            const timer = setInterval(() => {
                if (useAppStore().socket) {
                    resolve(useAppStore().socket)
                    clearInterval(timer)
                }
            }, 10)
        })
    }
    return new Promise<Socket>((resolve, reject) => {
        const app = useAppStore()
        if (app.socket) {
            resolve(app.socket)
            return
        }
        if (!window.WebSocket && window.MozWebSocket) {
            window.WebSocket = window.MozWebSocket
        }
        if (!window.WebSocket) {
            window.$error('检测到当前浏览器不支持Websocket')
            reject('检测到当前浏览器不支持Websocket')
            return
        }
        JBoltApi.get('/api/user/info/myOnlineUserToken', null, { ignoreMsg: true }).then(
            ({ error, result }) => {
                if (error) {
                    window.$error('初始化Websocket服务时失败，请联系管理员')
                    reject('Websocket初始化时获取Token失败')
                    return
                }
                const token = result.data
                localStg.set(Keys.WEBSOCKET_TOKEN, token)

                let url: string = `${state.protocal}://${state.host}${state.ctx}/websocket.ws/${token}`
                if (state.tenantSn) {
                    url += `?jbtenantsn=${state.tenantSn}`
                }

                state.url = url

                connect()

                getSocket().then((socket) => resolve(socket))
            }
        )
    })
}

function connect() {
    try {
        state.isConnecting = true
        console.log('Websocket 尝试连接...')

        const ws = new WebSocket(state.url)
        ws.binaryType = state.binaryType as BinaryType
        //发生异常
        ws.onerror = function (e: any) {
            useAppStore().setSocket(null)
            state.isOk = false
            closeHeartBeatCheck()
            setTimeout(() => {
                connect()
            }, state.reconnInterval)
            WebSocketHandler.onerror(e)
        }

        //连接成功
        ws.onopen = function (e: any) {
            state.isOk = true
            state.isConnecting = false
            useAppStore().setSocket(ws)
            state.lastInteractionTime = new Date().getTime()
            startHeartBeatCheck()

            registerEvent(ws)
            WebSocketHandler.onopen(e)
        }
    } catch (e) {
        console.error('Websocket连接失败', e)
    }
}

/**
 * 注册事件
 * @param ws
 */
function registerEvent(ws: Socket) {
    //连接关闭处理
    ws.onclose = function (event: any) {
        state.isOk = false
        state.isConnecting = false
        useAppStore().setSocket(null)
        //关闭心跳检测
        clearInterval(state.pingInterval!)
        //执行重连
        connect()
        WebSocketHandler.onclose(event)
    }

    ws.onmessage = function (event: any) {
        WebSocketHandler.onmessage(event)
        state.lastInteractionTime = new Date().getTime()
    }
}

/**
 * 心跳检测
 */
function startHeartBeatCheck() {
    if (state.pingInterval == null) {
        console.debug('开启WebSocket心跳检测...')
        state.pingInterval = window.setInterval(() => {
            ping()
        }, state.heartbeatSendInterval)
    }
}

/**
 * 关闭心跳检测
 */
function closeHeartBeatCheck() {
    if (state.pingInterval) {
        console.debug('关闭WebSocket心跳检测...')
        clearInterval(state.pingInterval)
        state.pingInterval = 0
    }
}

/**
 * 执行心跳ping
 */
function ping() {
    // console.debug('JBolt WebSocket check ping...')
    const iv = new Date().getTime() - state.lastInteractionTime // 已经多久没发消息了 // 单位：秒
    if (state.heartbeatSendInterval + iv >= state.heartbeatTimeout) {
        // console.debug('JBolt websocket ping doing...')
        sendCommand('ping')
        // console.debug('JBolt websocket ping done...')
    } else {
        // console.debug('JBolt websocket ping cancel...')
    }
}

/**
 * 执行发送指令到服务器
 * @param command 指令
 * @param data 数据
 * @param to 接收者
 */
function sendCommand(command: string, data?: any, to: string = '-1') {
    send(MsgType.COMMAND, command, data, to)
}
/**
 * 发送文本到服务器
 * @param data 数据
 * @param to 接收者
 */
function sendText(data: string, to: string = '-1') {
    send(MsgType.TEXT, null, data, to)
}

/**
 * 发送消息到服务器
 * @param msgType: MsgType
 * @param command
 * @param data
 * @param to
 */
function send(
    msgType: MsgType = MsgType.TEXT,
    command: string | null,
    data?: any,
    to: string = '-1'
) {
    const socket = useAppStore().socket
    if (state.isOk && socket) {
        if (command && msgType != MsgType.COMMAND) {
            msgType = MsgType.COMMAND
        }
        const msg = JSON.stringify({
            type: msgType,
            command,
            data,
            to
        })
        console.debug('JBolt WebSocket发送数据：', msg)
        socket.send(msg)
    } else {
        console.error('WebSocket尚未初始化或状态异常，无法发送数据')
    }
}

export function useWebsocket() {
    async function init() {
        console.log('JBolt websocket v' + state.version + ' starting...')
        let protocol = window.location.protocol.split(':')[0]
        if (protocol == 'https') {
            state.protocal = 'wss'
        } else {
            state.protocal = protocol = 'ws'
        }
        if (state.host.endsWith('/')) {
            state.host = state.host.slice(0, -1)
        }
        await getSocket()
    }

    /**
     * 关闭socket
     */
    function close() {
        const app = useAppStore()
        if (app.socket) {
            app.socket.close()
            app.setSocket(null)
        }
    }

    return {
        init,
        sendCommand,
        sendText,
        send,
        close
    }
}

export enum MsgType {
    COMMAND = 1,
    COMMAND_RET = 2,
    TEXT = 3,
    FILE = 4
}
